﻿/* Copyright (C) 2008-2009 Jorson.WHY
 * All right reserved
 * ********** Project Info ***********
 * Project Name : Common
 * NameSpace : MyLibrary
 * Filename : DynamicEntity
 * Create Time : 4/16/2008 7:18:14 PM
 * ********** Environment Info *******
 * Machine Name : EA-01504-11220
 * CLR Version : 2.0.50727.1433
 */

namespace MyLibrary.Tools
{
    #region System Using
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Xml;
    using System.Xml.Schema;
    using System.Reflection;
    using System.Reflection.Emit;
    #endregion

    /// <summary>
    /// 构建动态类
    /// </summary>
    public class DynamicEntity : IDisposable
    {
        public enum Visibily
        {
            Public = 0,
            Protected = 1,
            Private = 2
        }

        #region 变量和属性
        private XmlDocument _xDoc = null;
        private string _entityName = "DefaultEntityName";
        private Visibily _visibily = Visibily.Public;
        private Dictionary<string, Type> _propertyCollection = null;
        private object _innerEntity = null;

        //public Dictionary<string, Type> Propertys
        //{
        //    get { return _propertyCollection; }
        //    set { _propertyCollection = value; }
        //}
        ///// <summary>
        ///// 构建动态类需要的XML定义文档
        ///// </summary>
        //public XmlDocument EntityDescriptionFile
        //{
        //    get { return _xDoc; }
        //    set { _xDoc = value; }
        //}

        #endregion

        #region 构造函数
        private DynamicEntity()
        {
            this._propertyCollection = new Dictionary<string, Type>();
        }

        public DynamicEntity(XmlDocument xDoc)
        {
            this._propertyCollection = new Dictionary<string, Type>();
            this._xDoc = xDoc;
            LoadDynamicEntitySetting();
        }

        public DynamicEntity(string xmlPath)
        {
            this._propertyCollection = new Dictionary<string, Type>();
            if (!System.IO.File.Exists(xmlPath)) throw new System.IO.FileNotFoundException("指定的配置文件未能找到");
            try
            {
                this._xDoc = new XmlDocument();
                this._xDoc.Load(xmlPath);
                LoadDynamicEntitySetting();
            }
            catch
            {
                throw;
            }
        }
        ~DynamicEntity()
        {
            this.dispose(false);
        }
        #endregion

        #region 索引器
        public object this[string name]
        {
            get
            {
                if (String.IsNullOrEmpty(name)) throw new ArgumentNullException("属性名称不能为空");
                if (_innerEntity == null)
                    CreateDynamicEntityInstance();
                return GetPropertyValue(name);
            }
            set
            {
                if (String.IsNullOrEmpty(name)) throw new ArgumentNullException("属性名称不能为空");
                if (_innerEntity == null)
                    CreateDynamicEntityInstance();
                SetPropertyValue(name, value);
            }
        }
        public object this[string name,int index]
        {
            get 
            {
                if (String.IsNullOrEmpty(name)) throw new ArgumentNullException("属性名称不能为空");
                if (_innerEntity == null)
                    CreateDynamicEntityInstance();
                return GetPropertyValues(name)[index]; 
            }
        }
        #endregion

        #region 获取和设置实体的属性
        private object[] GetPropertyValues(string name)
        {
            Type type = _innerEntity.GetType();
            PropertyInfo property = type.GetProperty(name.ToUpper());
            if (property == null) throw new ArgumentException("没有找到对应的属性");
            if (!property.PropertyType.IsArray) throw new ArgumentException("指定的属性不是集合类型");

            object[] values = (object[])property.GetValue(_innerEntity, null);
            return values;
        }

        private object GetPropertyValue(string name)
        {
            Type type = _innerEntity.GetType();
            PropertyInfo property = type.GetProperty(name.ToUpper());
            if (property == null) throw new ArgumentException("没有找到对应的属性");

            object value = property.GetValue(_innerEntity, null);
            return value;
        }

        private void SetPropertyValue(string name, object value)
        {
            Type type = _innerEntity.GetType();
            PropertyInfo property = type.GetProperty(name.ToUpper());
            if (value.GetType() != property.PropertyType) throw new ArgumentException("设置值的类型与值类型不匹配");
            property.SetValue(_innerEntity, value, null);
        }
        #endregion

        #region TODO:验证输入的XML的正确性
        internal void ValidateXml(XmlDocument xDoc)
        {

        }
        #endregion

        #region 动态构建实体类
        /// <summary>
        /// 动态实体类型建立者
        /// </summary>
        /// <param name="entityName">实体名称</param>
        /// <param name="propertyinfo">属性信息</param>
        /// <param name="visiable">可见性</param>
        /// <returns>动态建立的类型</returns>
        private Type DynamicEntityTypeBuilder(string entityName, Dictionary<string, Type> propertyinfo, Visibily visiable)
        {
            AssemblyName asmname = new AssemblyName();
            asmname.Name = "Assembly_" + entityName;
            asmname.Version = new Version("1.0.0");

            //确定Assembly是在运行时使用的
            AssemblyBuilderAccess access = AssemblyBuilderAccess.Run;
            AssemblyBuilder builder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmname, access);
            ModuleBuilder module = builder.DefineDynamicModule("Module_" + entityName);

            TypeAttributes tAttri = TypeAttributes.Class;
            if(visiable == Visibily.Private || visiable == Visibily.Protected)
                tAttri = tAttri | TypeAttributes.NotPublic;
            else
                tAttri = tAttri | TypeAttributes.Public;
            TypeBuilder typeBuilder = module.DefineType("Type_" + entityName, tAttri);

            //定义Field
            FieldBuilder fieldBuilder;
            PropertyBuilder propertyBuilder;
            MethodAttributes mAttri = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;
            MethodBuilder getPropertyBuilder;
            MethodBuilder setPropertyBuilder;

            ILGenerator getIL;
            ILGenerator setIL;

            //开始根据需要定义属性和字段
            foreach (KeyValuePair<string, Type> pair in propertyinfo)
            {
                fieldBuilder = typeBuilder.DefineField(pair.Key, pair.Value, FieldAttributes.Private);
                propertyBuilder = typeBuilder.DefineProperty(pair.Key.ToUpper(), PropertyAttributes.HasDefault, pair.Value, null);

                getPropertyBuilder = typeBuilder.DefineMethod("get_" + pair.Key, mAttri, pair.Value, Type.EmptyTypes);
                getIL = getPropertyBuilder.GetILGenerator();
                getIL.Emit(OpCodes.Ldarg_0);
                getIL.Emit(OpCodes.Ldfld, fieldBuilder);
                getIL.Emit(OpCodes.Ret);

                setPropertyBuilder = typeBuilder.DefineMethod("set_" + pair.Key, mAttri, null, new Type[] { pair.Value });
                setIL = setPropertyBuilder.GetILGenerator();
                setIL.Emit(OpCodes.Ldarg_0);
                setIL.Emit(OpCodes.Ldarg_1);
                setIL.Emit(OpCodes.Stfld, fieldBuilder);
                setIL.Emit(OpCodes.Ret);

                propertyBuilder.SetSetMethod(setPropertyBuilder);
                propertyBuilder.SetGetMethod(getPropertyBuilder);
            }
            return typeBuilder.CreateType();
        }
        /// <summary>
        /// 建立动态实体
        /// </summary>
        private void CreateDynamicEntityInstance()
        {
            Type entityType = DynamicEntityTypeBuilder(this._entityName, this._propertyCollection, this._visibily);
            _innerEntity = Activator.CreateInstance(entityType);
        }
        #endregion

        #region 从配置文件中获取类的配置
        /// <summary>
        /// 载入配置信息
        /// </summary>
        private void LoadDynamicEntitySetting()
        {
            if (this._xDoc == null) throw new ArgumentNullException("输入的XML对象类型为空");
            if (this._propertyCollection == null) throw new ArgumentNullException("内部对象为空,请检查构造函数");

            XmlNode root = this._xDoc.SelectSingleNode("//Entity");
            if (root == null) throw new ArgumentNullException("没有找到正确的跟路径");

            if (root.Attributes["name"] != null) this._entityName = root.Attributes["name"].Value; else this._entityName = "DefaultName";
            string temp = String.Empty;
            if ((temp = root.Attributes["visiable"].Value) != null)
            {
                if (temp.ToLower() == "public")
                    this._visibily = Visibily.Public;
                else if (temp.ToLower() == "protected")
                    this._visibily = Visibily.Protected;
                else if (temp.ToLower() == "private")
                    this._visibily = Visibily.Private;
                else
                    this._visibily = Visibily.Public;
            }

            //读取属性配置
            XmlNodeList nodeList = this._xDoc.SelectNodes("/Entity/Propertys//Property");
            if (nodeList.Count == 0) throw new ArgumentException("在配置文件中没有找到属性配置项");
            string pName = String.Empty;
            string PType = String.Empty;
            Type type;

            foreach (XmlNode node in nodeList)
            {
                if (node.Attributes["name"] == null) continue;
                if (node.Attributes["type"] == null) continue;

                pName = node.Attributes["name"].Value;
                PType = node.Attributes["type"].Value;

                if (PType == "custom" && node.Attributes["customtype"] != null)
                {
                    type = StringToType(PType, node.Attributes["customtype"].Value);
                    this._propertyCollection.Add(pName, type);
                }
                else
                {
                    type = StringToType(PType);
                    this._propertyCollection.Add(pName, type);
                }
            }
        }

        private Type StringToType(string typeName, string type)
        {
            //处理自定的情况
            if (String.IsNullOrEmpty(typeName)) throw new ArgumentNullException("属性类型为空");
            if (typeName.Trim().ToLower() != "custom") throw new ArgumentException("函数只处理自定义类型的情况");

            return Type.GetType(type, true);
        }

        private Type StringToType(string typeName)
        {
            if (String.IsNullOrEmpty(typeName)) throw new ArgumentNullException("属性类型为空");
            try
            {
                switch (typeName.Trim().ToLower())
                {
                    case "string": return typeof(String);
                    case "string[]": return typeof(String[]);
                    case "int": return typeof(Int32);
                    case "int[]": return typeof(Int32[]);
                    case "long": return typeof(Int64);
                    case "long[]": return typeof(Int64[]);
                    case "datetime": return typeof(DateTime);
                    case "datetime[]": return typeof(DateTime[]);
                    case "double": return typeof(Double);
                    case "double[]": return typeof(Double[]);
                    case "float": return typeof(float);
                    case "float[]": return typeof(float[]);
                    case "decimal": return typeof(Decimal);
                    case "decimal[]": return typeof(Decimal[]);
                    case "bool": return typeof(Boolean);
                    default: return typeof(String);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        #endregion

        #region IDisposable Members

        private void dispose(bool disposing)
        {
            if (disposing)
            {
                if (this._innerEntity != null)
                {
                    GC.SuppressFinalize(_innerEntity);
                    this._innerEntity = null;
                }
                if (this._propertyCollection != null)
                    this._propertyCollection.Clear();
            }
        }
        public void Dispose()
        {
            dispose(true);
        }

        #endregion
    }
}
